package com.zhongan.gateway.common.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONObject;

/**
 * Created by Marco 加签/验签工具类
 */
public class SecurityUtil {

    //    private static final Logger logger            = LoggerFactory.getLogger(CryptUtil.class);

    private static final String SIGN_TYPE_RSA          = "RSA";

    private static final String CHARSET_UTF_8          = "UTF-8";
    private static final String ALGORITHM              = "DESede";
    private static final String SIGN_ALGORITHMS        = "SHA1WithRSA";

    private static final int    MAX_ENCRYPT_BLOCK_1024 = 117;

    private static final int    MAX_DECRYPT_BLOCK_1024 = 128;

    private static final int    MAX_ENCRYPT_BLOCK_2048 = 245;

    private static final int    MAX_DECRYPT_BLOCK_2048 = 256;

    private static final int    MAX_ENCRYPT_LIMIT      = 300;
    private static final int    MAX_DECRYPT_LIMIT      = 1000;

    public static String rsaEncrypt(String content, String publicKey) throws Exception {
        PublicKey pubKey = getPublicKeyFromX509(SIGN_TYPE_RSA, new ByteArrayInputStream(publicKey.getBytes()));
        Cipher cipher = Cipher.getInstance(SIGN_TYPE_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] data = content.getBytes(CHARSET_UTF_8);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;

        int maxEncryptBlock = MAX_ENCRYPT_BLOCK_1024;

        if (publicKey.length() > MAX_ENCRYPT_LIMIT) {
            maxEncryptBlock = MAX_ENCRYPT_BLOCK_2048;
        }
        // 对数据分段加密  
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > maxEncryptBlock) {
                cache = cipher.doFinal(data, offSet, maxEncryptBlock);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * maxEncryptBlock;
        }
        byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
        out.close();

        return new String(encryptedData, CHARSET_UTF_8);
    }

    public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        StringWriter writer = new StringWriter();
        StreamUtils.io(new InputStreamReader(ins), writer);

        byte[] encodedKey = writer.toString().getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
        if (ins == null || StringUtils.isEmpty(algorithm)) {
            return null;
        }

        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        byte[] encodedKey = StreamUtils.readText(ins).getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    /**
     * 私钥解密<br>
     * 
     * @param content 待解密内容
     * @param privateKey 私钥
     * @param charset 字符集，如UTF-8, GBK, GB2312
     * @return 明文内容
     * @throws Exception
     */
    public static String rsaDecrypt(String content, String privateKey) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));
            Cipher cipher = Cipher.getInstance(SIGN_TYPE_RSA);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedData = Base64.decodeBase64(content.getBytes(CHARSET_UTF_8));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            int maxDecryptBlock = MAX_DECRYPT_BLOCK_1024;

            if (privateKey.length() > MAX_DECRYPT_LIMIT) {
                maxDecryptBlock = MAX_DECRYPT_BLOCK_2048;
            }
            // 对数据分段解密  
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > maxDecryptBlock) {
                    cache = cipher.doFinal(encryptedData, offSet, maxDecryptBlock);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * maxDecryptBlock;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();

            return new String(decryptedData, CHARSET_UTF_8);
        } catch (Exception e) {
            throw new Exception("rsaDecrypt fail:EncodeContent = " + content, e);
        }
    }

    public static String desEncrypt(String src, String key) throws Exception {
        try {

            //生成密钥  
            SecretKey deskey = new SecretKeySpec(build3DesKey(key), ALGORITHM);
            //加密  
            Cipher c1 = Cipher.getInstance(ALGORITHM);
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            byte[] b = c1.doFinal(src.getBytes(CHARSET_UTF_8));//在单一方面的加密或解密  

            return new String(Base64.encodeBase64(b), CHARSET_UTF_8);
        } catch (Exception e) {
            throw new Exception("desEncrypt failure:EncodeContent = " + src, e);
        }

    }

    public static String rsaSign(String content, String privateKey) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA,
                    new ByteArrayInputStream(privateKey.getBytes(CHARSET_UTF_8)));

            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);

            signature.initSign(priKey);

            signature.update(content.getBytes(CHARSET_UTF_8));

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new Exception("rsa sign fail:RSAcontent = " + content, e);
        }
    }

    public static boolean rsaCheckSign(String content, String sign, String publicKey) throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509(SIGN_TYPE_RSA, new ByteArrayInputStream(publicKey.getBytes()));

            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);

            signature.initVerify(pubKey);

            signature.update(content.getBytes(CHARSET_UTF_8));

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new Exception("rsa checksign fail:RSAcontent=" + content + ",sign=" + sign, e);
        }
    }

    //keybyte为加密密钥，长度为24字节      
    //src为加密后的缓冲区  
    public static String desDecrypt(String src, String key) throws Exception {

        try {

            //生成密钥  
            SecretKey deskey = new SecretKeySpec(build3DesKey(key), ALGORITHM);
            //解密  
            Cipher c1 = Cipher.getInstance(ALGORITHM);
            c1.init(Cipher.DECRYPT_MODE, deskey);
            byte[] b = c1.doFinal(Base64.decodeBase64(src.getBytes(CHARSET_UTF_8)));//在单一方面的加密或解密  
            return new String(b, CHARSET_UTF_8);
        } catch (java.lang.Exception e) {
            throw new Exception("desDecrypt failure:EncodeContent = " + src, e);
        }

    }

    public static String md5Sign(String content) throws Exception {
        String genSign = "";
        try {
            genSign = DigestUtils.md5Hex(content.getBytes(CHARSET_UTF_8));//指定编码格式
            // logger.info("加签密文：" + genSign);
        } catch (Exception e) {
            throw new Exception("md5sign fail:EncodeContent = " + content, e);
        }
        return genSign;
    }

    public static boolean md5CheckSign(String content, String sign) throws Exception {
        try {
            String s = md5Sign(content);
            return sign.equals(s);
        } catch (Exception e) {
            throw new Exception("md5 checksign fail:content=" + content + ",sign=" + sign, e);
        }
    }

    public static byte[] build3DesKey(String keyStr) throws UnsupportedEncodingException {
        byte[] key = new byte[24]; //声明一个24位的字节数组，默认里面都是0
        byte[] temp = keyStr.getBytes("UTF-8"); //将字符串转成字节数组

        /*
         * 执行数组拷贝 System.arraycopy(源数组，从源数组哪里开始拷贝，目标数组，拷贝多少位)
         */
        if (key.length > temp.length) {
            //如果temp不够24位，则拷贝temp数组整个长度的内容到key数组中
            System.arraycopy(temp, 0, key, 0, temp.length);
        } else {
            //如果temp大于24位，则拷贝temp数组24个长度的内容到key数组中
            System.arraycopy(temp, 0, key, 0, key.length);
        }
        return key;
    }

    public static String getSortedStr(Object obj) throws Exception {
        Map<String, Object> map = beanToMap(obj);

        return getSortedStr(map);
    }

    public static String getSortedStr(Map<String, Object> map) throws Exception {

        TreeMap<String, Object> treeMap = new TreeMap<String, Object>(map);
        String treeMapStr = JSONObject.toJSONString(treeMap);
        return treeMapStr;
    }

    public static Map<String, Object> beanToMap(Object bean) throws Exception {
        Map<String, Object> returnMap = new HashMap<String, Object>();
        try {
            Class<?> type = bean.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            //循环获取bean属性和值放入map中
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                //忽略class和signValue属性
                if (!"class".equals(propertyName) && !"signValue".equals(propertyName)) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean, new Object[0]);

                    returnMap.put(propertyName, result);
                }
            }
        } catch (Exception e) {
            throw new Exception("beanToMap失败", e);

        }
        return returnMap;
    }

    //    public static void main(String[] args) throws Exception {
    //        String content = " PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));";
    //        String pk_1 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+pdSTLSCgCupLwJgcKfHISIxy9WFRfkG2w1O2gH7PLPnMKMnzks6jWbG+Lqhd/YgGklR5QXCB7LO72wm5vVkjxY3K49cl0apqpDOlJdzVetp5SfZ9fzCaTqbm+nCfaCzHOu/62dTRH4UmGeiq1/haUkARUy0nvzJ4CK4uWcW3ewIDAQAB";
    //        String prk_1 = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL6l1JMtIKAK6kvAmBwp8chIjHL1YVF+QbbDU7aAfs8s+cwoyfOSzqNZsb4uqF39iAaSVHlBcIHss7vbCbm9WSPFjcrj1yXRqmqkM6Ul3NV62nlJ9n1/MJpOpub6cJ9oLMc67/rZ1NEfhSYZ6KrX+FpSQBFTLSe/MngIri5Zxbd7AgMBAAECgYB7qXk/lhAJf8sGJAEGk7Z/YX+oub3MbIcXk49jW+nlXJkTcom8cseScYW3hPA1N1TaXmRo2qetvum+uo+OaWG9kCpVrPmSA/9n/wHdtk71ibLMp84sZlfdWB8b6lyx6Jnb+uYTSjVbvOKV9X1q+PUjOYbWtPTti4ay9vxMLZGsIQJBAPUh4FMU5bFx3uZzeS6TSWVkqnaN4nISfdwLgh0lYp0oFAWWQA+R64qyJBhKGsU242e8hI8/URhrXnIlWR9pBvkCQQDHGZU5aBTagywC35ETgLwZpkimRh8vcSuD3RhTmcOh44PC+mws++Jmc7iMUw4EeHm00gEpds1pK4imVDRm+YsTAkEA0ZOP9Zg1NPtVBhQNtCtKofxp3Qg7ZSm4H0xbhs2yvQhJ7ejihi1Mgv0A7R9WOexOih9b+hi5HbaWPeL+imNCCQJAa+DqwB4rHnLwh+YHP6ADS4qT1rQr9ZLGVz6XDcVFARS7sgvqTT114LlXB4GBGN8GYvle886RYijac/UwJ5OSzQJBAOd5jmwNqeIq49cT5nzole6LUIv2ZFIW+AHm89QH5pyPqDmcq0ktHVzMZeC/1oHwn70hndAnmT6BAVrr4TOEkWg=";
    //        String pk_2 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgdGeBjybzqRzvKH8Md9nKOBMYalNZDMnDnKb+1l2qz1dkgWRH+9MoRIQtZeFyjYTVUpsudpq38B2vhEojcb58HssedRAQPNoqjq2jxUwBHC1StTKmffBl+Xfm/V7MBJFU/OgP8oyzc1DZ8X0M/+w0B+szeiyQMi7c8XfK4wVUK20PhbnwqEU0DQCN6fvESGhM8g/bNRfwuaXjrnlwR59EPcq9DarEcNwShLqdbfslRMEr3W0KSLb6HU1xieyYIF+0suz3yRhOJEb2vJIr0KLiS6dl+q5gRJbtEr8unIBsuxXTrcpJ1HVUkLILj8ay8mgtZu+rNEeKc7OONFN1YVjQIDAQAB";
    //        String prk_2 = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyB0Z4GPJvOpHO8ofwx32co4ExhqU1kMycOcpv7WXarPV2SBZEf70yhEhC1l4XKNhNVSmy52mrfwHa+ESiNxvnweyx51EBA82iqOraPFTAEcLVK1MqZ98GX5d+b9XswEkVT86A/yjLNzUNnxfQz/7DQH6zN6LJAyLtzxd8rjBVQrbQ+FufCoRTQNAI3p+8RIaEzyD9s1F/C5peOueXBHn0Q9yr0NqsRw3BKEup1t+yVEwSvdbQpItvodTXGJ7JggX7Sy7PfJGE4kRva8kivQouJLp2X6rmBElu0Svy6cgGy7FdOtyknUdVSQsguPxrLyaC1m76s0R4pzs440U3VhWNAgMBAAECggEAZVOYu0BPtBkc5jHKR7PzfZTeMocVPxCkNIL8AoqW/0iUCPdsnR4rXDZIPqpuMTZRLmq7ahUmYrLOJohJ4B4HpvgMNRGYXECC6QLFcOMHxa4WO2gGVMt798+ulQsoMjEbJ6MdwPP5jvhkHNWhI3tO4Giz9GNFJq2/UqUdFBy8a7my8tzQmjR/dzTddPpVuxJZXrHZsGCe+Q3wKwfFX3PRmHXtZtCIxJQhuoK37yzt0i5+Ss7ieULjhkl5hiYVoxH6j4IJiT3axhpmeQXMA+88yxgsKoCZuC8SZhzrZUMO7f3o4UV4D4aGmo0geJOLbh8KLh0GegEm9ikKJNoCAbsCrQKBgQDYsMYYTTw91VhBlcdUTg29DedjUro9Q6G7C7ZA2DSBTsAepdRSZMr1fOsVC2eZopUIdUdlkoLXkxNWTErKUJR/7nYxTf6ixlb7+mIqs7BbBCYCgRjEThTO7W3GXb02PEzglhPueHTLZMKAd860i4Y19JYHWpTLqCKX3v13qg4KFwKBgQDSUwMjSFPAPPe2Yw9suHcXT+oep8sRBbsqAbSraucpTAs4dm6EcSa3RCIS2ydAoc4gVjV5dPHTwMuhyWepIYPsgXyUZU81Zmu8gJDSSUe+MuPJOlW+fLKCfidQWNnvxhVg+h1t+FZ6yvq9i3cuDNffzwYj1qtdThzTMAgj0pr3+wKBgDVQwVznXAQmwxFYBfRjRBQZIK6ag0JhqqB4ED7CM6EBPFoOGUCwlhNyyfCinrn5e8vZ91gxhUn/YQmhueEvlGopxSD6fMWWIX7cK10wWxr52VRSaUKbp5kGaDUfiVeX3J6BrqPFNY952jV7xPs9PSd3Z5alnejCFMjjT+Aqkku/AoGAZq7YjVU2blhVp9ndIfs9SUheyPCql3jw06VZi4b5rJPPtpU7h7WmbTr6V7JfC5Lzi54LJW0O048A28/0qAIQ4lqlc92yOiPy3Wj3S3Mzb4xw4UF1szY2ALeOgg+e9w0CSwJRc+sWbYTDvd5lJTpn+oCcQsr/huIn+IMuti6QMhkCgYEAqcAbz4H7mxK5BKublzEg4OqVJyJPH4lGTthY5zpOfyW0URtja1dsbDIgfuzpxy4+AEVKJxZXvBQi/WU41f+PtFaecfe8jG6inA4njLPCUwqJpBhBGrm8HaOHCRVUCX9doJ/vNIa+GrzILiVMCCb0ihfAUpuvBlcTb/XQZaHOxUI=";
    //        String key = "2012PinganVitality075522628888ForShenZhenBelter075561869839";
    //        String re = desEncrypt(pk_1, content);
    //
    //        System.out.println(re);
    //        System.out.println();
    //        String re2 = desDecrypt(pk_1, re);
    //
    //        System.out.println(re2);
    //    }

}
